package org.bjf.modules.sys.service;

import static org.bjf.modules.sys.listenner.LiveBeanPostProcessor.PERMISSION_MAP;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bjf.cache.CacheNameConfig.RedisCacheEnum.Names;
import org.bjf.exception.CommMsgCode;
import org.bjf.exception.ServiceException;
import org.bjf.modules.core.service.BaseService;
import org.bjf.modules.sys.bean.ControllerResource;
import org.bjf.modules.sys.bean.SysAcl;
import org.bjf.modules.sys.bean.SysRole;
import org.bjf.modules.sys.enums.AclEnum.PrincipalType;
import org.bjf.modules.sys.enums.AclEnum.ResourceType;
import org.bjf.modules.sys.enums.PermissionMethod;
import org.bjf.modules.sys.mapper.SysAclMapper;
import org.bjf.modules.sys.query.SysAclQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * @author bjf
 * @date 2017/11/27
 */
@Service
@Slf4j
public class SysAclService extends BaseService<SysAclMapper, SysAcl, SysAclQuery> {

  @Autowired
  private SysUserService sysUserService;
  @Autowired
  private SysRoleService roleService;

  @Override
  protected EntityWrapper<SysAcl> buildQuery(SysAclQuery q) {
    if (q == null) {
      return null;
    }
    EntityWrapper<SysAcl> ew = new EntityWrapper<>();
    ew.eq(q.getPrincipalType() != null, "principal_type", q.getPrincipalType());
    ew.eq(q.getPrincipalId() != null, "principal_id", q.getPrincipalId());
    ew.eq(q.getResourceType() != null, "resource_type", q.getResourceType());
    ew.eq(StringUtils.isNotBlank(q.getResourceId()), "resource_id", q.getResourceId());
    ew.in(!CollectionUtils.isEmpty(q.getPrincipalIdList()), "principal_id", q.getPrincipalIdList());
    return ew;
  }

  /**
   * 检查用户是否用controller资源权限
   *
   * @param sysUserId 用户id
   * @param controllerName 类型
   * @param permissionMethod 匹配的权限
   */
  @Cacheable(value = Names.SYS, key = "'live:acl:hasControllerPermission:' + #sysUserId + ':' + #controllerName+':' + #permissionMethod.name()")
  public boolean hasControllerPermission(Long sysUserId, String controllerName,
      PermissionMethod permissionMethod) {
    //===1.取用户所有角色
    List<SysRole> roles = roleService.listByUser(sysUserId);
    if (roles.isEmpty()) {
      return Boolean.FALSE;
    }
    //===2.根据角色和资源id取出acl
    List<SysAcl> aclList = new ArrayList<>();
    for (SysRole role : roles) {
      aclList
          .addAll(listRolePermission(role.getSysRoleId(), ResourceType.CONTROLLER, controllerName));
    }
    if (aclList.isEmpty()) {
      return Boolean.FALSE;
    }
    //===3.判断是否有acl权限
    for (SysAcl acl : aclList) {
      boolean has = this.hasPermission(acl, permissionMethod.getPos());
      if (has) {
        return Boolean.TRUE;
      }
    }
    return Boolean.FALSE;
  }

  /**
   * 根据角色查询acl
   */
  @Cacheable(value = Names.SYS, key = "'live:acl:listRolePermission:' + #roleId + ':' + #resourceType.type +':' + #resourceId")
  public List<SysAcl> listRolePermission(Long roleId, ResourceType resourceType,
      String resourceId) {
    //===1.根据角色取权限
    SysAclQuery query = new SysAclQuery();
    query.setPrincipalType(PrincipalType.ROLE.getType());
    query.setPrincipalId(roleId);
    query.setResourceType(resourceType.getType());
    if (StringUtils.isNotBlank(resourceId)) {
      query.setResourceId(resourceId);
    }

    return this.listAll(query);
  }

  /**
   * 全量更新角色的controller权限
   */
  @Transactional(rollbackFor = Exception.class)
  public void updateControllerPermission(Long roleId,
      List<ControllerResource> controllerPermission) {
    //===1.先删除角色全部权限
    EntityWrapper<SysAcl> ew = new EntityWrapper<>();
    ew.eq("principal_type", PrincipalType.ROLE.getType());
    ew.eq("principal_id", roleId);
    ew.eq("resource_type", ResourceType.CONTROLLER.getType());
    this.delete(ew);

    //===2.新增角色权限
    for (ControllerResource permission : controllerPermission) {
      if (!PERMISSION_MAP.containsKey(permission.getResSn())) {
        continue;
      }
      SysAcl acl = new SysAcl();
      acl.setPrincipalType(PrincipalType.ROLE.getType());
      acl.setPrincipalId(roleId);
      acl.setResourceType(ResourceType.CONTROLLER.getType());
      String resourceId = PERMISSION_MAP.get(permission.getResSn()).getResSn();
      acl.setResourceId(resourceId);
      acl.setAclState(Integer.parseInt(permission.getAcl(), 2));
      this.add(acl);

    }
    //===3.删除用户缓存
    sysUserService.clearUserCache();
  }


  /**
   * 设置权限
   *
   * @param position 权限位置
   * @param permit 是否允许访问
   */
  public void setPermission(SysAcl acl, int position, boolean permit) {
    if (position < 0 || position > 31) {
      throw new ServiceException(CommMsgCode.SYSTEM_ERROR, "权限位置只能在0~31之间");
    }
    int state = acl.getAclState() != null ? acl.getAclState() : 0;
    acl.setAclState(calcBit(state, position, permit));
  }

  /**
   * 位运算设置值
   *
   * @param state 旧值
   * @param position 设置的位置
   * @param permit 是否允许访问 1-是 0-否
   */
  private int calcBit(int state, int position, boolean permit) {
    int tmp = 1;
    tmp = tmp << position;
    if (permit) {
      // 或运算
      state = state | tmp;
    } else {
      // 取反，再与运算
      tmp = ~tmp;
      state = state & tmp;
    }
    return state;
  }

  /**
   * 检查权限
   *
   * @param position 权限位置
   */
  private boolean hasPermission(SysAcl acl, int position) {
    Integer state = acl.getAclState();
    if (state == null || state == 0) {
      return Boolean.FALSE;
    }
    // 移位然后与运算
    int tmp = 1;
    tmp = tmp << position;
    return (state & tmp) > 0;
  }
}
